#include "Socket.hpp"
#include "Thread.hpp"
#include <iostream>
#include <string>
#include <functional>
#include <memory>

using namespace NetWork;
using ThreadModule::Thread;
using std::cout;
using std::endl;

using func_t = std::function<std::string(std::shared_ptr<Socket>)>;

class TcpServer
{
public:
    TcpServer(uint16_t port, func_t handler_request) : _port(port), _handler_request(handler_request)
    {
        _listen_socket = std::make_shared<TcpSocket>();
        _listen_socket->BuildListenSocketMethod(port);
    }
    ~TcpServer() = default;

    void Loop()
    {
        while (true)
        {
            std::string peerip;
            uint16_t peerport;

            auto new_socket = _listen_socket->AcceptSocketOrDie(&peerip, &peerport);
            if (new_socket)
            {
                cout << "New connection from " << peerip << ":" << peerport << endl;
            }

            pthread_t tid;
            ThreadData* th_data = new ThreadData(new_socket, this);
            pthread_create(&tid, nullptr, ThreadFunc, th_data);
        }
    }

private:
    class ThreadData
    {
    public:
        using ptr = std::shared_ptr<ThreadData>;
    public:
        ThreadData(const std::shared_ptr<Socket> client_socket, TcpServer* server): _client_socket(client_socket), _server(server) {}

        std::shared_ptr<Socket> getClientSocket()
        {
            return _client_socket;
        }
        TcpServer* getServer()
        {
            return _server;
        }
    private:
        std::shared_ptr<Socket> _client_socket;
        TcpServer* _server;
    };

    static void* ThreadFunc(void* arg)
    {
        pthread_detach(pthread_self());

        auto td = static_cast<ThreadData*>(arg);
        std::shared_ptr<Socket> client_socket = td->getClientSocket();
        TcpServer* server = td->getServer();
        while(true)
        {
            server->_handler_request(client_socket);  // handle request callback function
        }
        client_socket->CloseSocket();

        return nullptr;
    }

private:
    std::shared_ptr<Socket> _listen_socket;
    uint16_t _port;
    func_t _handler_request;
};